#pragma once

#include <cassert>
#include <functional>
#include <unordered_map>
#include "epoller.hpp"
#include "Sock.hpp"
#include "Util.hpp"
#include "Protocol.hpp"

// 默认值
static const uint16_t default_port = 8080;
static const int default_buffer_size = 1024;

// 前置声明
class Connect;
class EpollServer;

using func = function<Response(const Request &)>;
using cb_func = function<void(Connect &)>;

class Connect
{
public:
    Connect()
    {
    }

    Connect(int fd, string ip, uint16_t port) : _fd(fd), _ip(ip), _port(port)
    {
    }

    void Register(cb_func recver, cb_func sender, cb_func excepter)
    {
        _recver = recver;
        _sender = sender;
        _excepter = excepter;
    }

    ~Connect()
    {
    }

public:
    int _fd;
    string _inbuffer;
    string _outbuffer;
    int _event;

    // 回调函数
    cb_func _recver;
    cb_func _sender;
    cb_func _excepter;

    // debug
    string _ip;
    uint16_t _port;

    // 回指指针
    EpollServer *_me;
};

class EpollServer
{
    static const int max_num = 64;

public:
    EpollServer(func callback, uint16_t port = default_port) : _callback(callback), _port(port)
    {
    }

    // 初始化
    void Init()
    {
        _listen_sock.Socket();
        _listen_sock.Bind(_port);
        _listen_sock.Listen();

        _ep.Create();

        AddConnect(_listen_sock.GetSock(), EPOLLIN | EPOLLET); // 添加到_connects,并设置为ET模式

        log_message(Debug, "Init is success");
    }

    // 判断读写
    bool EnableReadWrite(Connect &con, bool w, bool r)
    {
        con._event = (con._event | (w ? EPOLLOUT : 0)) | (con._event | (r ? EPOLLIN : 0));
        return _ep.AddModEvent(con._event, con._fd, EPOLL_CTL_MOD);
    }

    void AddConnect(int fd, int event, string ip = "127.0.0.7", uint16_t port = default_port)
    {
        // 判断该fd是否是ET模式，是的话设置该fd为非阻塞模式
        if (event & EPOLLET)
        {
            Util::NonBlocking(fd);
        }
        // 为fd创建Connect以及insert _connects
        Connect con(fd, ip, port);
        //_connects.insert({_listen_sock.GetSock(), con});
        _connects[fd] = con;
        
        if (fd == _listen_sock.GetSock())
        {
            _connects[fd].Register(bind(&EpollServer::Accepter, this, placeholders::_1), nullptr, nullptr);
        }
        else
        {
            _connects[fd].Register(bind(&EpollServer::Recv, this, placeholders::_1),
                                   bind(&EpollServer::Send, this, placeholders::_1),
                                   bind(&EpollServer::Except, this, placeholders::_1));
        }

        con._event = event;
        con._me = this;
        

        bool n = _ep.AddModEvent(event, fd, EPOLL_CTL_ADD);
        assert(n);
        (void)n;
    }

    void Accepter(Connect &con)
    {
        do
        {
            int err = 0;
            string client_ip;
            uint16_t client_port;
            int fd = _listen_sock.Accept(client_ip, client_port, err);

            if (fd > 0)
            {
                log_message(Debug, "接收到来自[%s:%d]的新连接, fd: %d", client_ip.c_str(), client_port, fd);
                AddConnect(fd, EPOLLIN | EPOLLET, client_ip, client_port); // 添加到_connects
            }
            else
            {
                // 表示目前已经读完数据了
                if (err == EAGAIN || err == EWOULDBLOCK)
                {
                    break;
                }
                // 表示遇到信号中断了，没事依然接着读
                else if (err == EINTR)
                {
                    continue;
                }
                else
                {
                    break;
                }
            }

        } while (con._event & EPOLLET);

        log_message(Debug, "Accepter done...");
    }

    bool RecvHelper(Connect &con)
    {
        bool ret = true;
        do
        {
            char buffer[default_buffer_size];
            int n = recv(con._fd, buffer, sizeof(buffer) - 1, 0);
            // 读取成功
            if (n > 0)
            {
                buffer[n] = '\0';
                con._inbuffer += buffer;
            }
            else if (n == 0)
            {
                con._excepter(con);
                ret = false;
                break;
            }
            else
            {
                if (errno == EAGAIN || errno == EWOULDBLOCK)
                {
                    break;
                }
                else if (errno == EINTR)
                {
                    continue;
                }
                else
                {
                    con._excepter(con);
                    ret = false;
                    break;
                }
            }
        } while (con._event & EPOLLET);

        return ret;
    }

    void RecvRequest(Connect &con)
    {
        bool quit = false;
        while (!quit)
        {
            string request;
            int n = ReadPackage(con._inbuffer, request);
            if (n > 0)
            {
                // 1.提取有效载荷
                request = SubHeaders(request, n);

                // 2.反序列化
                Request req;
                req.Deserialize(request);

                // 3.业务处理
                Response res = _callback(req);
                string response;
                // 4.序列化
                res.Serialize(response);

                // 5.添加报头
                response = AddHeaders(response);

                con._outbuffer += response;
            }
            else
            {
                quit = true;
            }
        }
    }

    void Recv(Connect &con)
    {
        if (!RecvHelper(con))
        {
            return;
        }
        RecvRequest(con);
        if (!con._outbuffer.empty())
        {
            con._sender(con);
        }
    }

    void Send(Connect &con)
    {
        int quit = true;
        do
        {
            int n = send(con._fd, con._outbuffer.c_str(), con._outbuffer.size(), 0);
            if (n > 0)
            {
                con._outbuffer.erase(0, n); // 已发送的数据删掉
                if (con._outbuffer.empty())
                {
                    break;
                }
            }
            else
            {
                if (errno == EAGAIN || errno == EWOULDBLOCK)
                {
                    break;
                }
                else if (errno == EINTR)
                {
                    continue;
                }
                else
                {
                    quit = false;
                    con._excepter(con);
                    break;
                }
            }
        } while (con._event & EPOLLET);

        if (!quit)
        {
            return;
        }
        if (con._outbuffer.empty())
        {
            EnableReadWrite(con, false, true);
        }
        else
        {
            EnableReadWrite(con, true, true);
        }
    }

    void Except(Connect &con)
    {
        // 1.从epoll中移除
        _ep.DelEvent(con._fd);

        // 2.从_connects中移除
        _connects.erase(con._fd);

        // 3.关闭文件描述符
        close(con._fd);

        log_message(Debug, "Except done...");
    }

    // 每一次执行
    void LoopOnce(int timeout)
    {
        int n = _ep.Wait(_data, max_num, timeout);
        for (int i = 0; i < n; ++i)
        {
            int fd = _data[i].data.fd;
            uint32_t event = _data[i].events;

            if ((event & EPOLLHUP) || (event & EPOLLERR))
            {
                event |= (EPOLLIN | EPOLLOUT);
            }

            if ((event & EPOLLIN) && ConIsExist(fd))
            {
                _connects[fd]._recver(_connects[fd]);
            }
            if ((event & EPOLLOUT) && ConIsExist(fd))
            {
                _connects[fd]._sender(_connects[fd]);
            }
        }
    }

    // 判断_connects中是否还存在该key的value
    bool ConIsExist(int fd)
    {
        return _connects.find(fd) != _connects.end();
    }

    // 启动
    void Start()
    {
        // 1. -1:阻塞等待
        // 2. 0:非阻塞等待
        // 3. >0:阻塞自定义事件后轮询一次
        // ps:单位毫秒
        int timeout = -1;
        while (true)
        {
            LoopOnce(timeout);
        }
    }

    ~EpollServer()
    {
    }

private:
    uint16_t _port;
    Sock _listen_sock;
    Epoll _ep;
    struct epoll_event _data[max_num];
    func _callback;
    unordered_map<int, Connect> _connects;
};